package com.fadis.client;



import com.fadis.FadisException;
import com.fadis.cmd.CmdDispatcher;
import com.fadis.cmd.ExistOption;
import com.fadis.cmd.FadisActionFrame;
import com.fadis.protocol.*;
import com.fadis.utils.FadisTuple;

import java.util.*;

/**
 * http://redisdoc.com/
 *
 * 1 集合
 * 2 单线程
 * 3 超时
 * 4 命令行
 * 5 日志
 * 6 持久化
 * 7 监视器
 * 8 数据库管理工具，远程修改等
 * 9 阻塞
 * 10 set if exists
 * 12 bitmap，geo，布隆过滤器等
 */
public class Ladis {

//    public static Long auth(String key, long seconds){
//        BaseRespProtocol resp = sendCmdToServer("expire", key, seconds + "");
//        return Resp.getLong(resp);
//    }


    public static Long del(String...keys){
        BaseRespProtocol resp = sendCmdToServer2("expire", keys);
        return Resp.getLong(resp);
    }


    public static Long expire(String key, long seconds){
        BaseRespProtocol resp = sendCmdToServer("expire", key, seconds + "");
        return Resp.getLong(resp);
    }

    public static Long persist(String key){
        BaseRespProtocol resp = sendCmdToServer("persist", key);
        return Resp.getLong(resp);
    }

    public static Long ttl(String key){
        BaseRespProtocol resp = sendCmdToServer("ttl", key);
        return Resp.getLong(resp);
    }

    public static Long pttl(String key){
        BaseRespProtocol resp = sendCmdToServer("pttl", key);
        return Resp.getLong(resp);
    }

    public static Long exists(String key){
        BaseRespProtocol resp = sendCmdToServer("exists", key);
        return Resp.getLong(resp);
    }
    
    /**
     * SET key value [EX seconds] [PX milliseconds] [NX|XX]
     *
     * SET key value EX seconds 【 SETEX 】  秒超时
     * SET key value PX milliseconds 【 PSETEX 】 毫秒超时
     * SET key value NX  【 SETNX 】  键不存在则设置
     * SET key value XX  【 SETX 】   键存在则设置
     *
     * @param key
     * @param value
     * @return  成功则返回OK，否则返回null（使用了nx，xx的情况），对应redis里返回的(nil)
     */
    public static String set(String key, String value, ExistOption existsOption, long expireMilliSeconds) {
        List<String> args = new ArrayList<>();
        args.add("set");
        args.add(key);
        args.add(value);

        if(existsOption == ExistOption.NX) args.add("nx");
        else if(existsOption == ExistOption.XX) args.add("xx");

        if(expireMilliSeconds > 0){
            args.add("px");
            args.add(expireMilliSeconds + "");
        }

        BaseRespProtocol resp = sendCmdToServer(args);
        return Resp.getStringOK(resp);

    }
    public static String set(String key, String value) {
        return set(key, value, ExistOption.IGNORE, 0);
    }

    /**
     * append key value
     * 追加或设置
     * @param key
     * @param value
     * @return 新字符串长度
     */
    public Long append(String key, String value) {
        BaseRespProtocol resp = sendCmdToServer("append", key, value);
        return Resp.getLong(resp);
    }

    /**
     * setrange key offset value
     * 按偏移量覆盖，不存在的字符串按空串处理，串长度小于偏移量的，offset之前会用"\x00"填充
     * @param key
     * @param value
     * @return 新字符串长度
     */
    public static Long setrange(String key, final long offset, final String value){
        BaseRespProtocol resp = sendCmdToServer("setrange", key, offset + "", value);
        return Resp.getLong(resp);
    }

    /**
     * GET key
     * @param key
     * @return value or null
     */
    public static String get(String key) {
        BaseRespProtocol resp = sendCmdToServer("get", key);
        return Resp.getString(resp);
    }

    /**
     * GETSET key newValue
     * @param key
     * @param value
     * @return 旧值，或者null，旧值类型错误会抛出异常
     */
    public static String getSet(String key, String value) {
        BaseRespProtocol resp = sendCmdToServer("getset", key, value);
        return Resp.getString(resp);
    }

    /**
     * strlen key
     * @param key
     * @return 字符串长度，key不存在则返回0，类型错误则抛异常
     */
    public static Long strlen(String key) {
        BaseRespProtocol resp = sendCmdToServer("strlen", key);
        return Resp.getLong(resp);
    }

    /**
     * GETRANGE greeting 0 4          # 返回索引0-4的字符，包括4
     *
     * 支持负数
     * 不支持回绕，即start必须在end之前，回绕返回空串
     * end超出，这截取到len-1
     * start超出，返回空串
     *
     * @param key
     * @param startOffset
     * @param endOffset
     * @return
     */
    public static String getrange(String key, long startOffset, long endOffset) {
        BaseRespProtocol resp = sendCmdToServer("getrange", key, startOffset+"", endOffset+"");
        return Resp.getString(resp);
    }

    public static Long incrBy(String key, long increment) {
        BaseRespProtocol resp = sendCmdToServer("incr", key, increment+"");
        return Resp.getLong(resp);
    }

    public static Double incrByFloat(String key, double increment) {
        BaseRespProtocol resp = sendCmdToServer("incrbyfloat", key, increment+"");
        return Resp.getDouble(resp);
    }

    /**
     * INCR key
     * 无则填0再+1
     * 有但非字符串，报错
     * @param key
     * @return
     */
    public static Long incr(String key) {
        BaseRespProtocol resp = sendCmdToServer("incr", key);
        return Resp.getLong(resp);
    }

    public static Long decrBy(String key, long decrement) {
        BaseRespProtocol resp = sendCmdToServer("decrby", key, decrement+"");
        return Resp.getLong(resp);
    }

    public static Long decr(String key) {
        BaseRespProtocol resp = sendCmdToServer("decr", key);
        return Resp.getLong(resp);
    }

    /**
     * MSET date "2012.3.30" time "11:00 a.m." weather "sunny"
     * @param keysvalues
     * @return OK
     */
    public static String mset(String... keysvalues) {
        List<String> args = new ArrayList<>();
        args.add("mset");
        for (String s: keysvalues){
            args.add(s);
        }
        BaseRespProtocol resp = sendCmdToServer(args);
        return Resp.getStringOK(resp);
    }

    /**
     * MSETNX rmdbs "MySQL" nosql "MongoDB" key-value-store "redis"
     *
     * 键不存在，则设置【必须所有键都不存在】
     * 都成功或都失败
     *
     * @param keysvalues
     * @return  1都成功 0都失败
     */
    public static Long msetnx(String... keysvalues) {
        List<String> args = new ArrayList<>();
        args.add("msetnx");
        for (String s: keysvalues){
            args.add(s);
        }
        BaseRespProtocol resp = sendCmdToServer(args);
        return Resp.getLong(resp);
    }

    /**
     * MGET date time weather
     * @param keys
     * @return
     */
    public static List<String> mget(String... keys) {
        List<String> args = new ArrayList<>();
        args.add("mget");
        for (String s: keys){
            args.add(s);
        }
        BaseRespProtocol resp = sendCmdToServer(args);
        return Resp.getStringList(resp);
    }


    /**
     * RPUSH key value1 value2
     * @param key
     * @param items
     * @return 列表长度
     */
    public static Long rpush(String key, String... items) {
        List<String> args = new ArrayList<>();
        args.add("rpush");
        args.add(key);
        for (String s: items){
            args.add(s);
        }
        BaseRespProtocol resp = sendCmdToServer(args);
        return Resp.getLong(resp);

    }

    public  static Long rpushx(String key, String... items) {
        List<String> args = new ArrayList<>();
        args.add("rpushx");
        args.add(key);
        for (String s: items){
            args.add(s);
        }
        BaseRespProtocol resp = sendCmdToServer(args);
        return Resp.getLong(resp);

    }

    /**
     * LPUSH key value1 value2
     * @param key
     * @param items
     * @return 列表长度
     */
    public static Long lpush(String key, String... items) {
        List<String> args = new ArrayList<>();
        args.add("lpush");
        args.add(key);
        for (String s: items){
            args.add(s);
        }
        BaseRespProtocol resp = sendCmdToServer(args);
        return Resp.getLong(resp);
    }

    public static Long lpushx(String key, String... items) {
        List<String> args = new ArrayList<>();
        args.add("lpushx");
        args.add(key);
        for (String s: items){
            args.add(s);
        }
        BaseRespProtocol resp = sendCmdToServer(args);
        return Resp.getLong(resp);
    }

    /**
     * LLEN job
     * @param key
     * @return 无或空返回0，如果key存在但非列表，报错
     */
    public static Long llen(String key) {
        BaseRespProtocol resp = sendCmdToServer("llen", key);
        return Resp.getLong(resp);
    }

    /**
     * LRANGE fp-language 0 0
     * @param key
     * @param start
     * @param stop
     * @return
     */
    public static List<String> lrange(String key, long start, long stop) {
        BaseRespProtocol resp = sendCmdToServer("lrange", key, start+"", stop+"");
        return Resp.getStringList(resp);
    }

    /**
     * 保留下标范围之内的元素
     * LTRIM alpha 1 -1
     * @param key
     * @param start
     * @param stop
     * @return 成功返回OK，无和空都成功，非列表报错
     */
    public static String ltrim(String key, long start, long stop) {
        BaseRespProtocol resp = sendCmdToServer("ltrim", key, start+"", stop+"");
        return Resp.getString(resp);
    }

    /**
     *
     * @param key
     * @param index
     * @return 指定元素或者null
     */
    public static String lindex(String key, long index) {
        BaseRespProtocol resp = sendCmdToServer("lindex", key, index+"");
        return Resp.getString(resp);
    }

    /**
     * LSET list 0 item
     * @param key
     * @param index
     * @param value
     * @return  操作成功返回 ok ，当 index 参数超出范围，或对一个空列表( key 不存在)进行 LSET 时，返回一个错误。
     */
    public static String lset(String key, long index, String value) {
        BaseRespProtocol resp = sendCmdToServer("lset", key, index+"", value);
        return Resp.getStringOK(resp);
    }

    /**
     * LREM greet 2 morning
     *
     * count > 0 : 从表头开始向表尾搜索，移除与 value 相等的元素，数量为 count
     * count < 0 : 从表尾开始向表头搜索，移除与 value 相等的元素，数量为 count 的绝对值
     * count = 0 : 移除表中所有与 value 相等的值
     *
     * @param key
     * @param count
     * @param value
     * @return 被移除元素的数量，无返回0
     */
    public static Long lrem(String key, long count, String value) {
        BaseRespProtocol resp = sendCmdToServer("lrem", key, count+"", value);
        return Resp.getLong(resp);
    }

    /**
     * LPOP course
     * 移除并返回列表 key 的头元素。
     * @param key
     * @return 头元素，无或空则null
     */
    public static String lpop(String key) {
        BaseRespProtocol resp = sendCmdToServer("lpop", key);
        return Resp.getString(resp);
    }

    /**
     * RPOP course
     * 移除并返回列表 key 的头元素。
     * @param key
     * @return 头元素，无或空则null
     */
    public static String rpop(String key) {
        BaseRespProtocol resp = sendCmdToServer("rpop", key);
        return Resp.getString(resp);
    }

    /**
     * RPOPLPUSH key_src key_dst
     * @param srckey
     * @param dstkey
     * @return 从srckey里rpop，并lpush到dstkey，然后返回此元素
     */
    public static String rpoplpush(String srckey, String dstkey) {
        BaseRespProtocol resp = sendCmdToServer("rpoplpush", srckey, dstkey);
        return Resp.getString(resp);
    }


    /**
     * HSET website google "www.g.cn"
     */
    public static Long hset(String key, String field, String value) {
        BaseRespProtocol resp = sendCmdToServer("hset", key, field, value);
        return Resp.getLong(resp);
    }

    /**
     * HSETNX website google "www.g.cn"
     * @param key
     * @param field
     * @param value
     * @return
     */
    public static Long hsetnx(String key, String field, String value) {
        BaseRespProtocol resp = sendCmdToServer("hsetnx", key, field, value);
        return Resp.getLong(resp);
    }

    public static String hget(String key, String field) {
        BaseRespProtocol resp = sendCmdToServer("hget", key, field);
        return Resp.getString(resp);
    }

    /**
     * HMSET website google www.google.com yahoo www.yahoo.com
     * @param key
     * @param hash
     * @return OK
     */
    public static String hmset(String key, Map<String, String> hash) {
        List<String> list = new ArrayList<>();
        list.add("hmset");
        list.add(key);

        for (String k : hash.keySet()) {
            list.add(k);
            list.add(hash.get(k));
        }

        BaseRespProtocol resp = sendCmdToServer(list);
        return Resp.getStringOK(resp);
    }


    /**
     * 注意，总是返回一个size = fields.length的数组
     * 如果map不存在，则返回list的元素都是null
     * @param key
     * @param fields
     * @return
     */
    public static List<String> hmget(String key, String... fields) {
        List<String> args = new ArrayList<>();
        args.add("hmget");
        for (String s: fields){
            args.add(s);
        }
        BaseRespProtocol resp = sendCmdToServer(args);
        return Resp.getStringList(resp);
    }

    /**
     * HINCRBY counter page_view 200
     * map无则新建
     * field无则新建，并设置为0
     * @param key
     * @param field
     * @param value
     * @return field最新的值
     */
    public static Long hincrBy(String key, String field, long value) {
        BaseRespProtocol resp = sendCmdToServer("hincrby", key, field, value+"");
        return Resp.getLong(resp);
    }

    /**
     * HINCRBYFLOAT counter page_view 0.1
     * map无则新建
     * field无则新建，并设置为0
     * @param key
     * @param field
     * @param value
     * @return field最新的值
     */
    public static Double hincrByFloat(String key, String field, double value) {
        BaseRespProtocol resp = sendCmdToServer("hincrbyfloat", key, field, value+"");
        return Resp.getDouble(resp);
    }

    public  static Boolean hexists(String key, String field) {
        BaseRespProtocol resp = sendCmdToServer("hexists", key, field);
        Long r = Resp.getLong(resp);
        return r != null && r == 1;
    }

    /**
     * HDEL key field1 field2
     * @param key
     * @param fields
     * @return
     */
    public  static Long hdel(String key, String... fields) {
        List<String> args = new ArrayList<>();
        args.add("hdel");
        for (String s: fields){
            args.add(s);
        }
        BaseRespProtocol resp = sendCmdToServer(args);
        return Resp.getLong(resp);
    }

    /**
     * hlen key
     * @param key
     * @return key的数量，无则0
     */
    public  static Long hlen(String key) {
        BaseRespProtocol resp = sendCmdToServer("hlen", key);
        return Resp.getLong(resp);
    }

    /**
     * hstrlen key field
     * @param key
     * @return key的数量，无则0
     */
    public static Long hstrlen(String key, String field) {
        BaseRespProtocol resp = sendCmdToServer("hstrlen", key, field);
        return Resp.getLong(resp);
    }

    /**
     * HKEYS key
     * @param key
     * @return 所有key，无或空都返回empty set
     */
    public  static Set<String> hkeys(String key) {
        BaseRespProtocol resp = sendCmdToServer("hkeys", key);
        Set<String> set = Resp.getStringSet(resp);
        return set;
    }

    /**
     * HVALS key
     * @param key
     * @return 所有value，无或空都返回empty list
     */
    public  static List<String> hvals(String key) {
        BaseRespProtocol resp = sendCmdToServer("hvals", key);
        return Resp.getStringList(resp);
    }

    /**
     * hgetall key
     * @param key
     * @return 所有key-value，无或空都返回empty map
     */
    public  static Map<String, String> hgetAll(String key) {

        BaseRespProtocol resp = sendCmdToServer("hgetall", key);
        List<String> list = Resp.getStringList(resp);

        Map<String, String> map = new HashMap<>();

        String t_key = null;
        for (int i = 1; i < list.size(); i++) {
            if(t_key == null) {
                t_key = list.get(i);
            } else{
                map.put(t_key, list.get(i));
                t_key = null;
            }
        }
        return map;
    }

    /**
     * sadd key item1 item2
     * @param key
     * @param members
     * @return
     */
    public  static Long sadd(String key, String... members) {
        List<String> args = new ArrayList<>();
        args.add("sadd");
        for (String s: members){
            args.add(s);
        }
        BaseRespProtocol resp = sendCmdToServer(args);
        return Resp.getLong(resp);
    }

    /**
     *
     * @param key
     * @return
     */
    public  static Set<String> smembers(String key) {
        BaseRespProtocol resp = sendCmdToServer("smembers", key);
        Set<String> set = Resp.getStringSet(resp);
        return set;
    }

    public  static Long srem(String key, String... members) {
        List<String> args = new ArrayList<>();
        args.add("srem");
        for (String s: members){
            args.add(s);
        }
        BaseRespProtocol resp = sendCmdToServer(args);
        return Resp.getLong(resp);
    }

    public  static String spop(String key) {
        BaseRespProtocol resp = sendCmdToServer("spop", key);
        return Resp.getString(resp);
    }

    public static  Set<String> spop(String key, long count) {
        BaseRespProtocol resp = sendCmdToServer("spop", key, count+"");
        return Resp.getStringSet(resp);
    }

    public  static String srandmember(String key) {
        BaseRespProtocol resp = sendCmdToServer("srandmember", key);
        return Resp.getString(resp);
    }

    public  static List<String> srandmember(String key, int count) {
        BaseRespProtocol resp = sendCmdToServer("srandmember", key, count+"");
        return Resp.getStringList(resp);
    }

    public  static Long smove(String srckey, String dstkey, String member) {
        BaseRespProtocol resp = sendCmdToServer("smove", srckey, dstkey, member);
        return Resp.getLong(resp);
    }

    public static  Long scard(String key) {
        BaseRespProtocol resp = sendCmdToServer("scard", key);
        return Resp.getLong(resp);
    }

    public static  Boolean sismember(String key, String member) {
        BaseRespProtocol resp = sendCmdToServer("sismember", key, member);
        Long r = Resp.getLong(resp);
        return r != null && r == 1;
    }

    public static  Set<String> sinter(String... keys) {

        List<String> args = new ArrayList<>();
        args.add("sinter");
        for (String s: keys){
            args.add(s);
        }
        BaseRespProtocol resp = sendCmdToServer(args);
        return Resp.getStringSet(resp);
    }

    public static  Long sinterstore(String dstkey, String... keys) {

        List<String> args = new ArrayList<>();
        args.add("sinterstore");
        for (String s: keys){
            args.add(s);
        }
        BaseRespProtocol resp = sendCmdToServer(args);
        return Resp.getLong(resp);
    }

    public static  Set<String> sunion(String... keys) {

        List<String> args = new ArrayList<>();
        args.add("sunion");
        for (String s: keys){
            args.add(s);
        }
        BaseRespProtocol resp = sendCmdToServer(args);
        return Resp.getStringSet(resp);
    }

    public static  Long sunionstore(String dstkey, String... keys) {

        List<String> args = new ArrayList<>();
        args.add("sunionstore");
        for (String s: keys){
            args.add(s);
        }
        BaseRespProtocol resp = sendCmdToServer(args);
        return Resp.getLong(resp);
    }

    public static  Set<String> sdiff(String... keys) {

        List<String> args = new ArrayList<>();
        args.add("sdiff");
        for (String s: keys){
            args.add(s);
        }
        BaseRespProtocol resp = sendCmdToServer(args);
        return Resp.getStringSet(resp);
    }

    public static  Long sdiffstore(String dstkey, String... keys) {

        List<String> args = new ArrayList<>();
        args.add("sdiffstore");
        for (String s: keys){
            args.add(s);
        }
        BaseRespProtocol resp = sendCmdToServer(args);
        return Resp.getLong(resp);
    }


    public static  Long zadd(String key, double score, String member) {
        Map<String, Double> scoreMembers = new HashMap<>();
        scoreMembers.put(member, score);
        return zadd(key, scoreMembers, ExistOption.IGNORE, false, false);
    }

    public static  Long zadd(String key, double score, String member, ExistOption existOption, boolean returnChangedCount, boolean updateByIncr) {
        Map<String, Double> scoreMembers = new HashMap<>();
        scoreMembers.put(member, score);
        return zadd(key, scoreMembers, existOption, returnChangedCount, updateByIncr);
    }

    public static  Long zadd(String key, Map<String, Double> scoreMembers) {
        return zadd(key, scoreMembers, ExistOption.IGNORE, false, false);
    }

    // ZADD key [NX|XX|none] [CH] [INCR] score member [score member ...]
    public static  Long zadd(String key, Map<String, Double> scoreMembers, ExistOption existOption, boolean returnChangedCount, boolean updateByIncr) {
        List<String> args = new ArrayList<>();
        args.add("zadd");
        args.add("key");
        if(existOption == ExistOption.NX) args.add("nx");
        if(existOption == ExistOption.XX) args.add("xx");
        if(returnChangedCount) args.add("ch");
        if(updateByIncr) args.add("incr");

        for (String s: scoreMembers.keySet()){
            args.add(scoreMembers.get(s) + "");
            args.add(s);
        }
        BaseRespProtocol resp = sendCmdToServer(args);
        return Resp.getLong(resp);
    }

    // ZINCRBY key increment member
    // zincrby set1 10 m1
    public static  Double zincrby(String key, double increment, String member) {
        BaseRespProtocol resp = sendCmdToServer("zincrby", key, increment+"", member);
        return Resp.getDouble(resp);
    }

    // ZRANK key member
    public static  Long zrank(String key, String member) {
        BaseRespProtocol resp = sendCmdToServer("zrank", key, member);
        return Resp.getLong(resp);
    }

    // ZREVRANK key member
    public static  Long zrevrank(String key, String member) {
        BaseRespProtocol resp = sendCmdToServer("zrevrank", key, member);
        return Resp.getLong(resp);
    }


    public static  Set<String> zrange(String key, long start, long stop) {
        return (Set<String>)zrangeByRank(key, start, stop, false, false);
    }
    public  static Set<FadisTuple> zrangeWithScores(String key, long start, long stop) {
        return (Set<FadisTuple>)zrangeByRank(key, start, stop, true, false);
    }
    public static  Set<String> zrevrange(String key, long start, long stop) {
        return (Set<String>)zrangeByRank(key, start, stop, false, true);
    }
    public static  Set<FadisTuple> zrevrangeWithScores(String key, long start, long stop) {
        return (Set<FadisTuple>)zrangeByRank(key, start, stop, true, true);
    }

    // ZRANGE key start stop [WITHSCORES]
    // ZREVRANGE key start stop [WITHSCORES]
    private static  Object zrangeByRank(String key, long start, long stop, boolean withScores, boolean reverse){
        List<String> args = new ArrayList<>();

        String cmd = "zrange";
        if(reverse) cmd = "zrevrange";
        args.add(cmd);
        args.add(key);
        args.add(start +"");
        args.add(stop + "");
        if(withScores) args.add("withscores");

        BaseRespProtocol resp = sendCmdToServer(args);
        List<String> list = Resp.getStringList(resp);
        if(list == null || list.size() == 0) return null;
        if(withScores){
            Set<FadisTuple> set = new HashSet<>();
            String member = null;
            for (int i = 0; i < list.size(); i++) {
                if(member == null){
                    member = list.get(i);
                }else{
                    double score = Double.parseDouble(list.get(i));
                    FadisTuple tuple = new FadisTuple(member, score);
                    set.add(tuple);
                    member = null;
                }
            }
            return set;
        }else{
            Set<String> set = new HashSet<>();
            set.addAll(list);
            return set;
        }
    }

    // ZCARD key
    public  static Long zcard(String key) {
        BaseRespProtocol resp = sendCmdToServer("zcard", key);
        return Resp.getLong(resp);
    }

    // ZSCORE key member
    public static  Double zscore(String key, String member) {
        BaseRespProtocol resp = sendCmdToServer("zscore", key, member);
        return Resp.getDouble(resp);
    }

    // ZCOUNT key min max
    public static  Long zcount(String key, double min, double max) {
        return zcount(key, min + "", max +"");
    }

    // ZCOUNT key min max
    // min = (5  开区间
    // min = [5 闭区间，默认
    // min = -inf 负无限  max = +inf 正无限
    public static  Long zcount(String key, String min, String max) {
        BaseRespProtocol resp = sendCmdToServer("zcount", key, min+"", max+"");
        return Resp.getLong(resp);
    }

    public static  Set<String> zrangeByScore(String key, double min, double max) {
        return (Set<String>)zrangebyscore(key, min+"", max+"", false, false, false, 0, 0);

    }

    public static  Set<String> zrangeByScore(String key, String min, String max) {
        return (Set<String>)zrangebyscore(key, min+"", max+"", false, false, false, 0, 0);

    }

    public static  Set<String> zrangeByScore(String key, double min, double max, int offset, int count) {
        return (Set<String>)zrangebyscore(key, min+"", max+"", false, false, true, offset, count);

    }

    public static  Set<String> zrangeByScore(String key, String min, String max, int offset, int count) {
        return (Set<String>)zrangebyscore(key, min+"", max+"", false, false, true, offset, count);

    }

    public static  Set<FadisTuple> zrangeByScoreWithScores(String key, double min, double max) {
        return (Set<FadisTuple>)zrangebyscore(key, min+"", max+"", true, false, false, 0, 0);

    }

    public static  Set<FadisTuple> zrangeByScoreWithScores(String key, String min, String max) {
        return (Set<FadisTuple>)zrangebyscore(key, min+"", max+"", true, false, false, 0, 0);

    }

    public static  Set<FadisTuple> zrangeByScoreWithScores(String key, double min, double max, int offset, int count) {
        return (Set<FadisTuple>)zrangebyscore(key, min+"", max+"", true, false, true, offset, count);

    }

    public static  Set<FadisTuple> zrangeByScoreWithScores(String key, String min, String max, int offset, int count) {
        return (Set<FadisTuple>)zrangebyscore(key, min+"", max+"", true, false, true, offset, count);

    }

    public static  Set<String> zrevrangeByScore(String key, double max, double min) {
        return (Set<String>)zrangebyscore(key, min+"", max+"", false, true, false, 0, 0);

    }

    public static  Set<String> zrevrangeByScore(String key, String max, String min) {
        return (Set<String>)zrangebyscore(key, min+"", max+"", false, true, false, 0, 0);

    }

    public static  Set<String> zrevrangeByScore(String key, double max, double min, int offset, int count) {
        return (Set<String>)zrangebyscore(key, min+"", max+"", false, true, true, offset, count);

    }

    public static  Set<FadisTuple> zrevrangeByScoreWithScores(String key, double max, double min) {
        return (Set<FadisTuple>)zrangebyscore(key, min+"", max+"", true, true, false, 0, 0);

    }

    public static  Set<FadisTuple> zrevrangeByScoreWithScores(String key, double max, double min, int offset, int count) {
        return (Set<FadisTuple>)zrangebyscore(key, min+"", max+"", true, true, true, offset, count);

    }

    public static  Set<FadisTuple> zrevrangeByScoreWithScores(String key, String max, String min, int offset, int count) {
        return (Set<FadisTuple>)zrangebyscore(key, min, max, true, true, true, offset, count);

    }

    public static  Set<String> zrevrangeByScore(String key, String max, String min, int offset, int count) {
        return (Set<String>)zrangebyscore(key, min, max, false, true, true, offset, count);

    }

    public static  Set<FadisTuple> zrevrangeByScoreWithScores(String key, String max, String min) {
        return (Set<FadisTuple>)zrangebyscore(key, min, max, true, true, false, 0, 0);
    }

    // ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
    // ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]
    private static  Object zrangebyscore(String key, String min, String max, boolean withScores, boolean reverse, boolean limit, long offset, long count){
        List<String> args = new ArrayList<>();

        String cmd = "zrangebyscore";
        if(reverse) cmd = "zrevrangebyscore";
        args.add(cmd);
        args.add(key);
        args.add(min);
        args.add(max);
        if(withScores) args.add("withscores");
        if(limit){
            args.add("limit");
            args.add(offset+"");
            args.add(count+"");
        }

        BaseRespProtocol resp = sendCmdToServer(args);
        List<String> list = Resp.getStringList(resp);
        if(list == null || list.size() == 0) return null;
        if(withScores){
            Set<FadisTuple> set = new HashSet<>();
            String member = null;
            for (int i = 0; i < list.size(); i++) {
                if(member == null){
                    member = list.get(i);
                }else{
                    double score = Double.parseDouble(list.get(i));
                    FadisTuple tuple = new FadisTuple(member, score);
                    set.add(tuple);
                    member = null;
                }
            }
            return set;
        }else{
            Set<String> set = new HashSet<>();
            set.addAll(list);
            return set;
        }
    }

    // ZREM key member [member …]
    public static  Long zrem(String key, String... members) {
        List<String> args = new ArrayList<>();
        args.add("zrem");
        args.add(key);
        for (int i = 0; i < members.length; i++) {
            args.add(members[i]);
        }
        BaseRespProtocol resp = sendCmdToServer(args);
        return Resp.getLong(resp);
    }

    // ZREMRANGEBYRANK key start stop
    public static  Long zremrangeByRank(String key, long start, long stop) {
        BaseRespProtocol resp = sendCmdToServer("zremrangebyrank", key, start+"", stop+"");
        return Resp.getLong(resp);
    }

    // ZREMRANGEBYSCORE key min max
    public static  Long zremrangeByScore(String key, double min, double max) {
        BaseRespProtocol resp = sendCmdToServer("zremrangebyscore", key, min+"", max+"");
        return Resp.getLong(resp);
    }

    // ZREMRANGEBYSCORE key min max
    public static  Long zremrangeByScore(String key, String min, String max) {
        BaseRespProtocol resp = sendCmdToServer("zremrangebyscore", key, min+"", max+"");
        return Resp.getLong(resp);
    }

    // ZUNIONSTORE destination numkeys key [key …] [WEIGHTS weight [weight …]] [AGGREGATE SUM|MIN|MAX]
    public static  Long zunionstore(String dstkey, String... sets) {
        List<String> setList = new ArrayList<>();
        for (int i = 0; i < sets.length; i++) {
            setList.add(sets[i]);
        }
        return zunionstore(dstkey, setList, null, null);
    }

    public static  Long zunionstore(String dstkey, List<String> sets, List<Double> weights, String aggregate) {
        List<String> args = new ArrayList<>();
        args.add("zunionstore");
        args.add(dstkey);
        args.add(sets.size() + "");
        for (int i = 0; i < sets.size(); i++) {
            args.add(sets.get(i));
        }
        if(weights != null){
            args.add("weights");
            for (int i = 0; i < sets.size(); i++) {
                args.add(weights.get(i) + "");
            }
        }
        if(aggregate != null){
            args.add("aggregate");
            args.add(aggregate);
        }
        BaseRespProtocol resp = sendCmdToServer(args);
        return Resp.getLong(resp);
    }


    // zinterstore destination numkeys key [key …] [WEIGHTS weight [weight …]] [AGGREGATE SUM|MIN|MAX]
    public static  Long zinterstore(String dstkey, String... sets) {
        List<String> setList = new ArrayList<>();
        for (int i = 0; i < sets.length; i++) {
            setList.add(sets[i]);
        }
        return zinterstore(dstkey, setList, null, null);
    }

    public static  Long zinterstore(String dstkey, List<String> sets, List<Double> weights, String aggregate) {
        List<String> args = new ArrayList<>();
        args.add("zinterstore");
        args.add(dstkey);
        args.add(sets.size() + "");
        for (int i = 0; i < sets.size(); i++) {
            args.add(sets.get(i));
        }
        if(weights != null){
            args.add("weights");
            for (int i = 0; i < sets.size(); i++) {
                args.add(weights.get(i) + "");
            }
        }
        if(aggregate != null){
            args.add("aggregate");
            args.add(aggregate);
        }
        BaseRespProtocol resp = sendCmdToServer(args);
        return Resp.getLong(resp);
    }

    // ZLEXCOUNT key min max
    // min和max支持开闭区间  ( [
    // min和max支持正负无限  + -
    // ZRANGEBYLEX <zset> - +
    public static  Long zlexcount(String key, String min, String max) {
        BaseRespProtocol resp = sendCmdToServer("zlexcount", key, min, max);
        return Resp.getLong(resp);
    }

    // ZRANGEBYLEX key min max [LIMIT offset count]
    // min和max支持开闭区间  ( [
    // min和max支持正负无限  + -
    // ZRANGEBYLEX <zset> - +
    public  static Set<String> zrangeByLex(String key, String min, String max, boolean reverse, boolean limit, int offset, int count) {
        List<String> args = new ArrayList<>();
        args.add(reverse ? "zrevrangebylex" : "zrangebylex");
        args.add(key);
        args.add(min);
        args.add(max);
        if(limit){
            args.add("limit");
            args.add(offset +"");
            args.add(count + "");
        }
        BaseRespProtocol resp = sendCmdToServer(args);
        return Resp.getStringSet(resp);
    }


    public static  Set<String> zrangeByLex(String key, String min, String max) {
        return zrangeByLex(key, min, max, false, false, 0, 0);
    }


    public static  Set<String> zrevrangeByLex(String key, String max, String min) {
        return zrangeByLex(key, min, max, true, false, 0, 0);

    }

    public  static Set<String> zrevrangeByLex(String key, String max, String min, int offset, int count) {
        return zrangeByLex(key, min, max, true, true, offset, count);

    }

    // ZREMRANGEBYLEX key min max
    // min和max支持开闭区间  ( [
    // min和max支持正负无限  + -
    // ZRANGEBYLEX <zset> - +
    public static  Long zremrangeByLex(String key, String min, String max) {
        BaseRespProtocol resp = sendCmdToServer("zremrangebylex", key, min, max);
        return Resp.getLong(resp);
    }

    // PFADD key element [element …]
    // 如果 HyperLogLog 的内部储存被修改了， 那么返回 1 ， 否则返回 0 。
    public static  Long pfadd(String key, String... elements) {
        List<String> args = new ArrayList<>();
        args.add("pfadd");
        args.add(key);
        for (int i = 0; i < elements.length; i++) {
            args.add(elements[i]);
        }
        BaseRespProtocol resp = sendCmdToServer(args);
        return Resp.getLong(resp);
    }

    // PFCOUNT key [key …]
    // 多个key相当于merge再pfcount，但merge结果是临时的
    public static  long pfcount(String... keys) {
        List<String> args = new ArrayList<>();
        args.add("pfcount");
        for (int i = 0; i < keys.length; i++) {
            args.add(keys[i]);
        }
        BaseRespProtocol resp = sendCmdToServer(args);
        return Resp.getLong(resp);
    }

    // PFMERGE destkey sourcekey [sourcekey …]
    // 返回OK
    public static  String pfmerge(String destkey, String... sourcekeys) {
        List<String> args = new ArrayList<>();
        args.add("pfmerge");
        args.add(destkey);
        for (int i = 0; i < sourcekeys.length; i++) {
            args.add(sourcekeys[i]);
        }
        BaseRespProtocol resp = sendCmdToServer(args);
        return Resp.getStringOK(resp);
    }

    public static  int[] bfadd(String key, String... elements) {
        List<String> args = new ArrayList<>();
        args.add(elements.length > 1 ? "bf.madd" : "bf.add");
        args.add(key);
        for (int i = 0; i < elements.length; i++) {
            args.add(elements[i]);
        }
        BaseRespProtocol resp = sendCmdToServer(args);
        if(elements.length == 1){
            long r = Resp.getLong(resp);
            return new int[]{(int) r};
        }else{
            List<String> list = Resp.getStringList(resp);
            int[] r = new int[list.size()];
            for (int i = 0; i < list.size(); i++) {
                r[i] = Integer.parseInt(list.get(i));
            }
            return r;
        }
    }

    public static  int[] bfexists(String key, String... elements) {
        List<String> args = new ArrayList<>();
        args.add(elements.length > 1 ? "bf.exists" : "bf.mexists");
        args.add(key);
        for (int i = 0; i < elements.length; i++) {
            args.add(elements[i]);
        }
        BaseRespProtocol resp = sendCmdToServer(args);
        if(elements.length == 1){
            long r = Resp.getLong(resp);
            return new int[]{(int) r};
        }else{
            List<String> list = Resp.getStringList(resp);
            int[] r = new int[list.size()];
            for (int i = 0; i < list.size(); i++) {
                r[i] = Integer.parseInt(list.get(i));
            }
            return r;
        }
    }

    private static BaseRespProtocol sendCmdToServer(String...parts){
        // 1 拼frame
        List<String> args = new ArrayList<>();
        for (int i = 0; i < parts.length; i++) {
            args.add(parts[i]);
        }
        return sendCmdToServer(args);
    }

    private static BaseRespProtocol sendCmdToServer2(Object...parts){
        List<String> args = new ArrayList<>();
        for (int i = 0; i < parts.length; i++) {
            if(parts[i].getClass().isArray()){
                Object[] arr = (Object[]) parts[i];
                for (Object o: arr){
                    args.add(parts[i] + "");
                }
            }else{
                args.add(parts[i] + "");
            }
        }
        return sendCmdToServer(args);
    }

    private static BaseRespProtocol sendCmdToServer(List<String> args){
        // 1 拼frame
        FadisActionFrame frame = new FadisActionFrame();
        frame.appendArgs(args);

        // 2 decode成resp指令
        String cmd2 = Resp.parseToCmd(frame);

        // 3 发送给server，获取返回的resp指令
//        String r = FakeRedisServer.run(FakeJedisUtils.newArrayList(cmd2));

        System.out.println("----------------------");
//        FadisLog.log("发给server：" + cmd2.replace("\r\n", "/r/n"));
        for (int i = 0; i < args.size(); i++) {
            System.out.print(args.get(i) + " ");
        }
        System.out.println("\n");
        String r = CmdDispatcher.run(cmd2);
//        FadisLog.log("server返回：" + r.replace("\r\n", "/r/n"));


        // 4 解析指令 -- error, null都直接处理
        BaseRespProtocol resp = CmdDecoder.getDefault().decodeCmd(r);
        System.out.println("server返回：");
        Resp.print(resp);
        System.out.println("----------------------");
        if(resp instanceof ErrorString){
            ErrorString str = (ErrorString) resp;
            throw new FadisException(4001, str.getStr());
        }else if(resp instanceof BulkString){
            if(((BulkString) resp).getStr() == null) return null;
        }else if(resp instanceof RespArray){
            if(((RespArray) resp).isNull()) return null;
        }
        return resp;
    }

//    public static void main(String[] args) {
//        String[] a = {"1","2"};
//        Object b = a;
//        if(b instanceof String[]){
//            Object[] c = (Object[]) b;
//            System.out.println(c);
//        }
//    }

}
